Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sometimes execResult is undefined. #858

Merged
merged 2 commits into from
May 7, 2019
Merged

Conversation

rimiti
Copy link
Contributor

@rimiti rimiti commented May 2, 2019

Hello,

Sometimes, execResult return is undefined. In this case, we just have to return.

Note: Commit updated.

Regards,

@rimiti rimiti changed the title WIP - Sometimes promise return nothing Sometimes promise return void (undefined) May 2, 2019
@rimiti
Copy link
Contributor Author

rimiti commented May 3, 2019

@luin Could you please merge this PR? 🙏

@luin
Copy link
Collaborator

luin commented May 3, 2019

@rimiti Thank you for the pull request. I'm wondering in which case the pipeline will return void?

@rimiti
Copy link
Contributor Author

rimiti commented May 3, 2019

It occurred when I had a transaction with some del actions. I can’t share you a snipped code because it’s from my client software and the logic is too complicated to extract you an example.

Otherwise the fix doesn’t break the CI. It cant be safely merged.

Note: if you’re looking for a maintainer, I can help you.

@luin
Copy link
Collaborator

luin commented May 4, 2019

I just did some tests on my side, but the results seem to always be arrays no matter what the commands were in the transaction. Here's a sample:

redis.multi().del('foo1').del('foo2').exec(console.log)

The output is:

null [ [ null, 1 ], [ null, 1 ] ]

Could you create a minimum reproducible sample for this fix?

@rimiti rimiti changed the title Sometimes promise return void (undefined) Sometimes execResult is undefined. May 4, 2019
@rimiti
Copy link
Contributor Author

rimiti commented May 4, 2019

I've re-run the tests with adding a console.log() when execResult is undefined:

if (typeof execResult === 'undefined') {         
    console.log(pipeline);
    return;
}

Output:

tests    |        ...
tests    |        [ Command {
tests    |            name: 'multi',
tests    |            transformed: true,
tests    |            isCustomCommand: false,
tests    |            replyEncoding: 'utf8',
tests    |            errorStack: undefined,
tests    |            args: [],
tests    |            callback: undefined,
tests    |            resolve: [Function],
tests    |            reject: [Function],
tests    |            promise: [Promise] },
tests    |          Command {
tests    |            name: 'srem',
tests    |            transformed: true,
tests    |            isCustomCommand: false,
tests    |            replyEncoding: 'utf8',
tests    |            errorStack: undefined,
tests    |            args: [Array],
tests    |            callback: undefined,
tests    |            resolve: [Function],
tests    |            reject: [Function],
tests    |            promise: [Promise] },
tests    |          Command {
tests    |            name: 'srem',
tests    |            transformed: true,
tests    |            isCustomCommand: false,
tests    |            replyEncoding: 'utf8',
tests    |            errorStack: undefined,
tests    |            args: [Array],
tests    |            callback: undefined,
tests    |            resolve: [Function],
tests    |            reject: [Function],
tests    |            promise: [Promise] },
tests    |          Command {
tests    |            name: 'del',
tests    |            transformed: true,
tests    |            isCustomCommand: false,
tests    |            replyEncoding: 'utf8',
tests    |            errorStack: undefined,
tests    |            args: [Array],
tests    |            callback: undefined,
tests    |            resolve: [Function],
tests    |            reject: [Function],
tests    |            promise: [Promise] },
tests    |          Command {
tests    |            name: 'del',
tests    |            transformed: true,
tests    |            isCustomCommand: false,
tests    |            replyEncoding: 'utf8',
tests    |            errorStack: undefined,
tests    |            args: [Array],
tests    |            callback: undefined,
tests    |            resolve: [Function],
tests    |            reject: [Function],
tests    |            promise: [Promise] },
tests    |          Command {
tests    |            name: 'srem',
tests    |            transformed: true,
tests    |            isCustomCommand: false,
tests    |            replyEncoding: 'utf8',
tests    |            errorStack: undefined,
tests    |            args: [Array],
tests    |            callback: undefined,
tests    |            resolve: [Function],
tests    |            reject: [Function],
tests    |            promise: [Promise] },
tests    |          Command {
tests    |            name: 'srem',
tests    |            transformed: true,
tests    |            isCustomCommand: false,
tests    |            replyEncoding: 'utf8',
tests    |            errorStack: undefined,
tests    |            args: [Array],
tests    |            callback: undefined,
tests    |            resolve: [Function],
tests    |            reject: [Function],
tests    |            promise: [Promise] },
tests    |          Command {
tests    |            name: 'exec',
tests    |            transformed: true,
tests    |            isCustomCommand: false,
tests    |            replyEncoding: null,
tests    |            errorStack: undefined,
tests    |            args: [],
tests    |            callback: undefined,
tests    |            resolve: [Function],
tests    |            reject: [Function],
tests    |            promise: [Promise] },
tests    |          Command {
tests    |            name: 'del',
tests    |            transformed: true,
tests    |            isCustomCommand: false,
tests    |            replyEncoding: 'utf8',
tests    |            errorStack: undefined,
tests    |            args: [Array],
tests    |            callback: undefined,
tests    |            resolve: [Function],
tests    |            reject: [Function],
tests    |            promise: [Promise] },
tests    |          Command {
tests    |            name: 'del',
tests    |            transformed: true,
tests    |            isCustomCommand: false,
tests    |            replyEncoding: 'utf8',
tests    |            errorStack: undefined,
tests    |            args: [Array],
tests    |            callback: undefined,
tests    |            resolve: [Function],
tests    |            reject: [Function],
tests    |            promise: [Promise] } ],
tests    |       _result:
tests    |        [ [ null, 'OK' ],
tests    |          [ null, 'QUEUED' ],
tests    |          [ null, 'QUEUED' ],
tests    |          [ null, 'QUEUED' ],
tests    |          [ null, 'QUEUED' ],
tests    |          [ null, 'QUEUED' ],
tests    |          [ null, 'QUEUED' ],
tests    |          [ null, [Array] ],
tests    |          undefined,
tests    |          undefined ],
tests    |       _transactions: 0,
tests    |       _shaToScript: {},
tests    |       resolve: [Function],
tests    |       reject: [Function],
tests    |       promise:
tests    |        Promise {
tests    |          [ [Array],
tests    |            [Array],
tests    |            [Array],
tests    |            [Array],
tests    |            [Array],
tests    |            [Array],
tests    |            [Array],
tests    |            [Array],
tests    |            undefined,
tests    |            undefined ] },
tests    |       exec: [Function],
tests    |       execBuffer: [Function],
tests    |       nodeifiedPromise: true,
tests    |       replyPending: 0 }".
tests    |
tests    |       at BufferedConsole.log (node_modules/@jest/console/build/BufferedConsole.js:199:10)
tests    |       at node_modules/ioredis/built/transaction.js:47:25

I hope it can help you.

@luin
Copy link
Collaborator

luin commented May 4, 2019

Thanks for the logs. I can finally reproduce the issue. The key point is to send other commands after exec():

const pipeline = redis.multi().del('foo1')
pipeline.exec(console.log) // TypeError: Cannot read property '0' of undefined
pipeline.del('foo3') // pipeline shouldn't be used to send any commands when the `exec()` has been called on it.

I think we should catch the exception (like this pr do) but re-throw it as a more meaningful error with a message like "Transaction resolved failed. Did you call any commands in a transaction after exec() had been called on that transaction?"

What do you think?

@rimiti
Copy link
Contributor Author

rimiti commented May 4, 2019

I think we should catch the exception (like this pr do) but re-throw it as a more meaningful error with a message like "Transaction resolved failed. Did you call any commands in a transaction after exec() had been called on that transaction?"

I think there are two solutions:

  • First: Throwing an error when we are in this case.
  • Second: Adding commands sent after exec() into the pipeline array.

I prefer the second solution because, it always works.

@luin
Copy link
Collaborator

luin commented May 4, 2019

  • First: Throwing an error when we are in this case.

This is the quickest solution for this issue.

  • Second: Adding commands sent after exec() into the pipeline array.

This breaks BC since in the past commands invoked after exec() will not be sent to Redis. So even we chose this way, it have to be happened in the next major release.

Another cons I can think of is allowing commands after exec() makes things obscure: Will these command be included in the transaction, or be excluded from the transaction?

@rimiti
Copy link
Contributor Author

rimiti commented May 4, 2019

Another cons I can think of is allowing commands after exec() makes things obscure: Will these command be included in the transaction, or be excluded from the transaction?

I think, if it's obscure, it's because this is not the right way to do that. After reflection, it'll be more clear to just throw an error instead of trying to bypass transaction logic.

What disturbs me, if I run the same tests with redis package, it's works and without error.
It may be interesting to check how to the redis package treat this case ?

@luin
Copy link
Collaborator

luin commented May 4, 2019

Just tried node_redis. The following code:

const m = r.multi().del('foo1')
m.exec(console.log)
m.del('foo2')

Both node_redis & ioredis will simply ignore del(foo2). node_redis returns [0, undefined] corresponding to del(foo1) and del(foo2). The result of del(foo2) being undefined is because the command was not sent to Redis.

I think in this case, simply throwing errors to let developers know that there are commands not being sent would be a fair solution.

@rimiti
Copy link
Contributor Author

rimiti commented May 4, 2019

Both node_redis & ioredis will simply ignore del(foo2). node_redis returns [0, undefined] corresponding to del(foo1) and del(foo2). The result of del(foo2) being undefined is because the command was not sent to Redis.

Oh, it's so a silent error... 😕
Totally agreed with you, throwing an error is best solution.

@rimiti
Copy link
Contributor Author

rimiti commented May 6, 2019

@luin Do you want I throw an error like that instead of the return?

Before

        if (typeof execResult === 'undefined') {
          return
        }

After

        if (typeof execResult === 'undefined') {
          throw new Error('Pipeline cannot be used to send any commands when the `exec()` has been called on it.');
        }

What do you think?

@luin
Copy link
Collaborator

luin commented May 6, 2019

Looks good to me!

@rimiti
Copy link
Contributor Author

rimiti commented May 7, 2019

It can be merged. 🎉

@luin luin merged commit 0c3ef01 into redis:master May 7, 2019
ioredis-robot pushed a commit that referenced this pull request May 7, 2019
## [4.9.3](v4.9.2...v4.9.3) (2019-05-07)

### Bug Fixes

* more meaningful errors when using pipeline after exec(). ([#858](#858)) ([0c3ef01](0c3ef01))
@rimiti rimiti deleted the fix-undefined-results branch May 7, 2019 08:20
@rimiti
Copy link
Contributor Author

rimiti commented May 7, 2019

@luin Could you please publish on npm this new version?

@luin
Copy link
Collaborator

luin commented May 7, 2019

@rimiti The new version has been published automatically when merged. I just add the 4.9.3 to the latest tag.

janus-dev87 added a commit to janus-dev87/ioredis-work that referenced this pull request Mar 1, 2024
## [4.9.3](redis/ioredis@v4.9.2...v4.9.3) (2019-05-07)

### Bug Fixes

* more meaningful errors when using pipeline after exec(). ([#858](redis/ioredis#858)) ([0c3ef01](redis/ioredis@0c3ef01))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants